#include "Socket.h"
#include "InetAddress.h"
#include "Logger.h"

#include <string>

Socket::Socket(int sockfd) : sockfd_(sockfd) {}

void Socket::Create() {
    sockfd_ = socket(AF_INET, SOCK_STREAM, 0);
}

void Socket::Bind(InetAddress address) {
    struct sockaddr_in addr = address.Address();
    int ret = bind(sockfd_, (struct sockaddr*)&addr, sizeof(addr));
    if (ret < 0) {
        LOG_ERROR("bind fail.");
    }
}

void Socket::Listen(int logback) {
    // listenfd_
    int ret = listen(sockfd_, logback);
    if (ret < 0) {
        LOG_ERROR("listen fail.");
    }
}

int Socket::Accept(InetAddress* address) {
    struct sockaddr_in addr;
    bzero(&addr, sizeof(addr));
    socklen_t len = sizeof(addr);
    int connfd = accept(sockfd_, (struct sockaddr*)&addr, &len);
    if (connfd < 0) {
        LOG_ERROR("accept fail.");
    }
    address->SetAddress(addr);
    LOG_INFO("Accept a new connection:%s", address->IpPort().c_str());
    return connfd;
}

void Socket::Shutdown() {
    shutdown(sockfd_, SHUT_WR);
}

// 设置套接字为非阻塞
bool Socket::SetNonBlock() {
    int fd = fcntl(sockfd_, F_GETFL);
    if (fd < 0)
        return false;
    fcntl(sockfd_, F_SETFL, fd | O_NONBLOCK);
    return true;
}

// 支持端口复用
void Socket::SetReuseAddress(bool on) {
    // 第二个参数表示选项定义的层次，SOL_SOCKET 表示基本套接口选项
    // 第三个参数是需要设置的选项名，SO_REUSEADDR 表示复用 ip 地址和端口，SO_REUSEPORT 可要可不要
    // 第四个参数指向变量或结构的指针，包含选项的新值
    // 第五个参数是变量的大小
    int opt = on ? 1 : 0;
    setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}

// 支持端口复用
void Socket::SetReusePort(bool on) {
    // 第二个参数表示选项定义的层次，SOL_SOCKET 表示基本套接口选项
    // 第三个参数是需要设置的选项名，SO_REUSEADDR 表示复用 ip 地址和端口，SO_REUSEPORT 可要可不要
    // 第四个参数指向变量或结构的指针，包含选项的新值
    // 第五个参数是变量的大小
    int opt = on ? 1 : 0;
    setsockopt(sockfd_, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
}

int Socket::Fd() {
    return sockfd_;
}

Socket::~Socket() {
    LOG_INFO("%d was closed", sockfd_);
    close(sockfd_);
}